package com.ran.java_gobang.game;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ran.java_gobang.JavaGobangApplication;
import com.ran.java_gobang.game.request.GameRequest;
import com.ran.java_gobang.game.response.GameResponse;
import com.ran.java_gobang.model.User;
import com.ran.java_gobang.serivce.UserService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.io.IOException;
import java.util.UUID;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: ran
 * Date: 2025-08-28
 * Time: 23:31
 */

@Data
@Slf4j
// 该类表示一个游戏房间
public class Room {
    // id使用字符串方便生成唯一值 使用UUID
    private String roomId;
    private User user1;
    private User user2;
    private int whiteUser; // 先手方的玩家id

    private static final int ROW = 15;
    private static final int COL = 15;

    // 表示棋盘
    // 1) 0 表示当前位置没棋子
    // 2) 1 表示 user1 落子位置
    // 3) 2 表示 user2 落子位置
    private int[][] board = new int[ROW][COL];

    public Room() {
        roomId = UUID.randomUUID().toString();
        // 通过入口类 context 来手动注入 OnlineUserManager 和 RoomManager
        onlineUserManager = JavaGobangApplication.context.getBean(OnlineUserManager.class);
        roomManager = JavaGobangApplication.context.getBean(RoomManager.class);
        userService = JavaGobangApplication.context.getBean(UserService.class);
    }


    /**
     * 处理一次落子的操作
     * 做的事情:
     * 1. 记录棋子位置
     * 2. 进行胜负判定
     * 3. 给客户端返回响应
     *
     * @param json 前端传过来的请求字符串
     */

    // 引入 ObjectMapper 转换 JSON
    private ObjectMapper objectMapper = new ObjectMapper();

    // 引入 OnlineUserManager 获取玩家对象 session
    private OnlineUserManager onlineUserManager;

    // 引入 RoomManager 用于房间销毁
    private RoomManager roomManager;

    private UserService userService;

    public void putChess(String reqJson) throws IOException {
      //  1. 记录棋子位置
        GameRequest request = objectMapper.readValue(reqJson,GameRequest.class);
        GameResponse response = new GameResponse();
        // 当前这个棋子是玩家1 还是 玩家2 落下, 根据当前玩家id来决定
        int chess = request.getUserId() == user1.getUserId() ? 1 : 2;
        int row = request.getRow();
        int col = request.getCol();
        if (board[row][col] != 0) {
            // 虽然客户端已经判定过重复落子情况, 但是为了稳健
            log.error("当前位置 ( "+row+", "+col+") 已经有棋子了!");
            return;
        }

        // 把棋子落到棋盘上
        board[row][col] = chess;

        // 2. 打印当前棋盘信息, 方便观察局势, 来判断胜负
        printBoard();

      //  3. 进行胜负判定
        int winner = checkWinner(row,col,chess);
      //  4. 给房间中的所有客户端返回响应
        response.setMessage("putChess");
        response.setUserId(request.getUserId());
        response.setRow(row);
        response.setCol(col);
        response.setWinner(winner);

        // 要想发送 websocket 数据, 要获取session 会话对象
        WebSocketSession session1 = onlineUserManager.getFromGameRoom(user1.getUserId());
        WebSocketSession session2 = onlineUserManager.getFromGameRoom(user2.getUserId());

        // 如果当前会话结束, 特殊处理
        if (session1 == null) {
            // 玩家1 掉线, 玩家2 胜利
            response.setWinner(user2.getUserId());
            log.error("玩家1: "+user1.getUserName()+"掉线! " + "玩家2: " + user2.getUserName() + "自动获胜");
        }
        if (session2 == null) {
            // 玩家2 掉线, 玩家1 胜利
            response.setWinner(user1.getUserId());
            log.error("玩家2: "+user2.getUserName()+"掉线! " + "玩家1: " + user1.getUserName() + "自动获胜");
        }

        // 相应构造成JSON字符串来传输
        String respJson = objectMapper.writeValueAsString(response);
        if (session1 != null) {
            session1.sendMessage(new TextMessage(respJson));
        }
        if (session2 != null) {
            session2.sendMessage(new TextMessage(respJson));
        }

        // 5. 如果胜负已分, 销毁房间, 从 map 中删除
        if (response.getWinner() != 0) {
            log.info("游戏结束, 房间id: " + roomId + ", 获胜方id: " + response.getWinner());
            // 平局
            if (response.getWinner() == -1) {
                userService.userDraw(user1.getUserId());
                userService.userDraw(user2.getUserId());
                roomManager.remove(roomId,user1.getUserId(),user2.getUserId());
                return;
            }
            // 更新获胜方与失败方id
            int winUserId = response.getWinner();
            int loseUserId = response.getWinner() == user1.getUserId() ? user2.getUserId() : user1.getUserId();
            userService.userWin(winUserId);
            userService.userLose(loseUserId);
            // 销毁房间
            roomManager.remove(roomId,user1.getUserId(),user2.getUserId());
        }
    }

    // 打印棋盘
    private void printBoard() {
        System.out.println("[打印棋盘信息] 房间ID: " + roomId);
        System.out.println("------------------------------------------------------------");
        for (int r = 0; r < ROW; r++) {
            for (int c = 0; c < COL; c++) {
                System.out.print(board[r][c] + " ");
            }
            System.out.println();
        }
    }

    //TODO 该方法来判断落子后是否分出胜负

    /**
     * 玩家1 获胜就返回 玩家1 的id, 玩家2 获胜 就返回 玩家2 的id
     * 胜负未分就返回 0
     * @param row 坐标
     * @param col 坐标
     * @return 胜负结果
     */
    private int checkWinner(int row, int col,int chess) {
        // 针对一行的情况, 判断五子连珠
        // 不仅要五个连一起, 而且要是同一个 chess

        // 0. 检查平局
        boolean draw = true; // 平局判断标志
        for (int i = 0; i < ROW ; i++) {
            for (int j = 0; j < COL; j++) {
                if (board[i][j] == 0) {
                    draw = false; // 当前棋盘还有空间, 还没有平局, 把平局标志设置为false
                    break;
                }
            }
        }
        // 如果平局走这一步
        if (draw) {
            return -1; // -1代表平局
        }

        // 1. 检查所有行
        for (int c = col - 4; c <= col; c++) {
            // 捕捉异常这里必须加在 for 循环里面, 可以保证每个位置都能判断
            try {
                if (board[row][c] == chess
                        && board[row][c + 1] == chess
                        && board[row][c + 2] == chess
                        && board[row][c + 3] == chess
                        && board[row][c + 4] == chess) {
                    // 构成了五子连珠
                    return chess == 1 ? user1.getUserId() : user2.getUserId();
                }
            }catch (ArrayIndexOutOfBoundsException e) {
                // 出现越界异常, 直接忽略, 继续判断
                continue;
            }
        }

        // 2. 检查所有列
        for (int r = row - 4; r <= row; r++) {
            try {
                if (board[r][col] == chess
                && board[r + 1][col] == chess
                && board[r + 2][col] == chess
                && board[r + 3][col] == chess
                && board[r + 4][col] == chess) {
                    return chess == 1 ? user1.getUserId() : user2.getUserId();
                }
            }catch (ArrayIndexOutOfBoundsException e) {
                continue;
            }
        }

        // 3. 检查左对角线
        for (int r = row - 4,c = col - 4; r <= row && c <= col; r++, c++) {
            try {
                if (board[r][c] == chess
                && board[r + 1][c + 1] == chess
                && board[r + 2][c + 2] == chess
                && board[r + 3][c + 3] == chess
                && board[r + 4][c + 4] == chess) {
                    return chess == 1 ? user1.getUserId() : user2.getUserId();
                }
            }catch (ArrayIndexOutOfBoundsException e) {
                continue;
            }
        }

        // 4. 检查右对角线
        for (int r = row - 4, c = col + 4; r <= row && c >= col; r++, c--) {
            try {
                if (board[r][c] == chess
                && board[r + 1][c - 1] == chess
                && board[r + 2][c - 2] == chess
                && board[r + 3][c - 3] == chess
                && board[r + 4][c - 4] == chess) {
                    return chess == 1 ? user1.getUserId() : user2.getUserId();
                }
            }catch (ArrayIndexOutOfBoundsException e) {
                continue;
            }
        }
        // 胜负未分, 返回 0
        return 0;
    }
}
